package com.ta.gulimall.ware.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ta.gulimall.ware.feign.SkuInfoFeignService;
import com.ta.gulimall.ware.service.WareOrderTaskDetailService;
import com.ta.gulimall.ware.service.WareOrderTaskService;
import com.ta.gulimall.ware.view.entity.PurchaseDetailEntity;
import com.ta.gulimall.ware.view.entity.WareOrderTaskDetailEntity;
import com.ta.gulimall.ware.view.entity.WareOrderTaskEntity;
import com.ta.gulimall.ware.view.vo.LockStoreRespVo;
import com.ta.gulimall.ware.view.vo.LockStoreVo;
import com.ta.gulimall.ware.view.vo.LockWareSkuVo;
import com.ta.gulimall.ware.view.vo.WareSkuVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ta.gulimall.common.utils.system.PageUtils;
import com.ta.gulimall.common.utils.system.Query;

import com.ta.gulimall.ware.dao.WareSkuDao;
import com.ta.gulimall.ware.view.entity.WareSkuEntity;
import com.ta.gulimall.ware.service.WareSkuService;
import org.springframework.transaction.annotation.Transactional;


@Service("wareSkuService")
public class WareSkuServiceImpl extends ServiceImpl<WareSkuDao, WareSkuEntity> implements WareSkuService {
    @Autowired
    SkuInfoFeignService skuInfoFeignService;
    @Autowired
    WareOrderTaskService wareOrderTaskService;
    @Autowired
    WareOrderTaskDetailService wareOrderTaskDetailService;

    @Override
    public PageUtils pageWareSkus(Map<String, Object> params) {
        String skuId = (String) params.get("skuId");
        String wareId = (String) params.get("wareId");

        LambdaQueryWrapper<WareSkuEntity> wrapper = new LambdaQueryWrapper<WareSkuEntity>()
                .eq(StrUtil.isNotBlank(skuId), WareSkuEntity::getSkuId, skuId).eq(StrUtil.isNotBlank(wareId), WareSkuEntity::getWareId, wareId);
        IPage<WareSkuEntity> page = this.page(new Query<WareSkuEntity>().getPage(params), wrapper);

        return new PageUtils(page);
    }

    @Override
    public void addWareSku(PurchaseDetailEntity purchaseDetail) {
        if(ObjectUtil.isNull(purchaseDetail)){
            throw new RuntimeException("错误的采购需求");
        }
        WareSkuEntity wareSkuEntity =  getWareSku(purchaseDetail.getSkuId(), purchaseDetail.getWareId());
        if (ObjectUtil.isNull(wareSkuEntity)) {
            Object data = skuInfoFeignService.getSkuInfo(purchaseDetail.getSkuId()).get("data");
            Map<String, Object> map = (Map<String, Object>) skuInfoFeignService.getSkuInfo(purchaseDetail.getSkuId()).get("data");
            wareSkuEntity = new WareSkuEntity()
                    .setSkuId(purchaseDetail.getSkuId())
                    .setWareId(purchaseDetail.getWareId())
                    .setStock(purchaseDetail.getSkuNum())
                    .setSkuName(map.get("skuName").toString());
        }else{
            wareSkuEntity.setStock(wareSkuEntity.getStock() + purchaseDetail.getSkuNum());
        }
        saveOrUpdate(wareSkuEntity);

    }

    @Override
    public Map<Long, Long> mapSkuWare(List<Long> skuIds) {
        Map<Long, Long> entityMap = baseMapper.mapWareSku(skuIds).entrySet().stream()
                .collect(Collectors.toMap(Map.Entry::getKey, v -> Long.valueOf(v.getValue().getStock())));
        return entityMap;
    }

    @Override
    @Transactional
    public LockStoreRespVo lockStore(LockStoreVo lockStoreVo) {
        Map<Long, WareSkuVo> lockStockMap = lockStoreVo.getLockStockMap();
        Map<Long, WareSkuEntity> entityMap = baseMapper.selectSkuIdLockStock(lockStockMap.keySet());

        attemptLock(lockStockMap, entityMap);
//        WareOrderTaskEntity wareOrderTaskEntity = saveWareLockTask(lockStoreVo);
//        saveWareLockTaskDetail(wareOrderTaskEntity, lockStockMap, entityMap);

        //分布式事务最终一致性
        List<LockWareSkuVo> lockWareSkuVos = entityMap.values().stream().map(wareSkuEntity -> BeanUtil.copyProperties(wareSkuEntity, LockWareSkuVo.class)).collect(Collectors.toList());
        return LockStoreRespVo.builder()
                    .orderId(lockStoreVo.getOrderId())
                    .lockWareSkuVos(lockWareSkuVos)
                    .build();
    }

    @Override
    public void unlockStore(LockStoreRespVo lockStoreRespVo) {
        List<LockWareSkuVo> lockWareSkuVos = lockStoreRespVo.getLockWareSkuVos();
        for (LockWareSkuVo lockWareSkuVo : lockWareSkuVos) {
            baseMapper.unlockStore(lockWareSkuVo);
        }
    }

    /**
     * 创建锁定库存工作单
     * @param lockStoreVo
     * @return
     */
    private WareOrderTaskEntity saveWareLockTask(LockStoreVo lockStoreVo) {
        WareOrderTaskEntity entity = WareOrderTaskEntity.builder()
                .orderId(lockStoreVo.getOrderId())
                .orderSn(lockStoreVo.getOrderSn())
                .build();

        wareOrderTaskService.save(entity);
        return entity;
    }

    /**
     * 保存锁定库存工作单详情
     * @param wareOrderTaskEntity
     * @param lockStockMap
     * @param entityMap
     */
    private void saveWareLockTaskDetail(WareOrderTaskEntity wareOrderTaskEntity, Map<Long, WareSkuVo> lockStockMap, Map<Long, WareSkuEntity> entityMap) {
        List<WareOrderTaskDetailEntity> wareOrderTaskDetailList = new ArrayList<>();

        for (Map.Entry<Long, WareSkuVo> wareSkuEntry : lockStockMap.entrySet()) {
            WareSkuVo wareSkuVo = wareSkuEntry.getValue();
            WareSkuEntity skuEntity = entityMap.get(wareSkuEntry.getKey());

            WareOrderTaskDetailEntity taskDetailEntity = WareOrderTaskDetailEntity.builder()
                    .skuId(wareSkuEntry.getKey())
                    .skuName(wareSkuVo.getSkuName())
                    .skuNum(wareSkuVo.getStock())
                    .taskId(wareOrderTaskEntity.getId())
                    .wareId(skuEntity.getWareId())
                    .lockStatus(1)
                    .build();
            wareOrderTaskDetailList.add(taskDetailEntity);
        }
        wareOrderTaskDetailService.saveOrUpdateBatch(wareOrderTaskDetailList);
    }

    /**
     * 锁定库存
     * @param lockStockMap
     * @param entityMap
     */
    private void attemptLock(Map<Long, WareSkuVo> lockStockMap, Map<Long, WareSkuEntity> entityMap) {
        //有skuId库存为null
        if(entityMap.size() != lockStockMap.size()){
            List<Long> skuId = lockStockMap.keySet().stream().filter(wareSkuId -> !entityMap.containsKey(wareSkuId)).collect(Collectors.toList());
            throw new RuntimeException(skuId + "库存不足" );
        }

        //skuId尝试锁定库存
        List<Long> skuId = entityMap.entrySet().stream()
                .filter(entry -> entry.getValue().getStock() - lockStockMap.get(entry.getKey()).getStock() < 0)
                .map(Map.Entry::getKey)
                .collect(Collectors.toList());
        if(CollUtil.isNotEmpty(skuId)){
            throw new RuntimeException(skuId + "库存不足");
        }

        //skuId均可锁定
        Collection<WareSkuEntity> wareSkuEntities = entityMap.values();
        List<WareSkuEntity> entities = new ArrayList<>();
        for (WareSkuEntity wareSkuEntity : wareSkuEntities) {
            WareSkuVo wareSkuVo = lockStockMap.get(wareSkuEntity.getSkuId());
            WareSkuEntity skuEntity = WareSkuEntity.builder()
                    .id(wareSkuEntity.getId())
                    .stockLocked(wareSkuEntity.getStockLocked() + wareSkuVo.getStock())
                    .build();
            entities.add(skuEntity);
        }

        saveOrUpdateBatch(entities);
    }

    private WareSkuEntity getWareSku(Long skuId, Long wareId) {
        LambdaQueryWrapper<WareSkuEntity> wrapper = new LambdaQueryWrapper<WareSkuEntity>()
                .eq(WareSkuEntity::getWareId, wareId).eq(WareSkuEntity::getSkuId, skuId);

        return getOne(wrapper);
    }


}